package impl

import (
	"context"
	"fmt"

	"gitee.com/liuxiaowei123/vblog-01/vblog-01/vblog/conf"
	"gitee.com/liuxiaowei123/vblog-01/vblog-01/vblog/exception"
	"gitee.com/liuxiaowei123/vblog-01/vblog-01/vblog/ioc"
	"gitee.com/liuxiaowei123/vblog-01/vblog-01/vblog/token"
	"gitee.com/liuxiaowei123/vblog-01/vblog-01/vblog/user"
	"gorm.io/gorm"
)

var _ token.Service = &TokenServiceImpl{}

// 注册到ioc
// 对象TokenServiceImpl实现了ioc.IocObject接口
func init() {
	// ioc.Controller().Registry(token.Appname,&TokenServiceImpl{} )
	ioc.Controller().Registry(&TokenServiceImpl{})
}

// 注册后实现初始化，所以可以把NewTokenServiceImpl()注释
func (t *TokenServiceImpl) Init() {
	t.db = conf.C.Mysql.Getconn().Debug()
	// 错误代码 :t.user = ioc.Controller().Get(token.Appname).(user.Service)
	t.user = ioc.Controller().Get(user.AppName).(user.Service)
}

func (t *TokenServiceImpl) Name() string {
	return token.Appname
}

// 定义token模块的实现类（控制器）
type TokenServiceImpl struct {
	// Token对象要存入数据库，所以依赖db
	db *gorm.DB
	// 依赖User模块，所以要定义此属性
	user user.Service
}

// 通过导入此包的方式，把TokenServiceImpl对象注册到ioc中

// 构造实现token接口的实例
// func NewTokenServiceImpl(userimpl user.Service) *TokenServiceImpl {
// 	return &TokenServiceImpl{
// 		db:   conf.C.Mysql.Getconn().Debug(),
// 		user: userimpl,
// 	}
// }
// 改进
// func NewTokenServiceImpl() *TokenServiceImpl {
// 	return &TokenServiceImpl{
// 		db:   conf.C.Mysql.Getconn().Debug(),
// 		user: ioc.Controller().Get(token.Appname).(user.Service),
// 	}
// }

// 登录接口(颁发Token)
func (i *TokenServiceImpl) Login(
	ctx context.Context, req *token.LoginRequest) (
	*token.Token, error) {
	// 1.查询用户
	d := user.NewDescribeUserRequestByUsername(req.Username)
	user, err := i.user.DescirbeUser(ctx, d)
	if err != nil {
		// 改进 return nil, exception.NewNotFound("用户名或密码不正确")
		// 把exception.NewNotFound("用户名或密码不正确") 定义到一个文件中
		return nil, token.AuthFailed

	}

	// 2.比对密码
	err = user.CheckPassword(req.Password)
	if err != nil {
		// 改进 return nil, exception.NewAuthFailed("用户名或密码不正确")
		return nil, token.AuthFailed
		// return nil, err
	}

	// 3.颁发token
	tk := token.NewToken()
	// 错误代码,用的不是req 而是user
	/*tk.UserName = req.Username
	tk.UserId = req.Password*/
	tk.UserName = user.Username
	// 没有加 tk.UserId = user.Id ，导致UserId=0,而查询不到用户，导致报错
	tk.UserId = user.Id

	// 4.存入token
	err = i.db.WithContext(ctx).Create(tk).Error
	if err != nil {
		return nil, err
	}

	// 颁发成功后,把之前的Token标记为失效,避免同一个用户多次登录，如何写？

	return tk, nil
}

// 退出接口(销毁Token)
func (i *TokenServiceImpl) Logout(
	ctx context.Context, req *token.LogoutRequest) error {
	return nil
}

// ValiateToken是用来校验Token，给后面gin的中间件使用
func (i *TokenServiceImpl) ValiateToken(
	ctx context.Context, req *token.ValiateTokenRequest) (
	*token.Token, error) {
	// 1.判断传入的token是否存在
	tk := token.NewToken()
	// 调用First(tk)方法，将会执行查询并将查询结果存储在tk指针指向的变量中
	err := i.db.WithContext(ctx).Where("access_token = ?", req.AccessToken).First(tk).Error
	if err != nil {
		// return nil, err
		return nil, exception.NewNotFound("token不存在")
	}
	// 2.判断token是否过期
	// 要用token实例来调 token方法的
	err = tk.IsExpired()
	if err != nil {
		return nil, err
	}

	// 3.补充用户信息
	// 查出用户登录时的role，并补充到token中，为了后面中间件鉴权使用
	// 错误代码：newuser := user.NewDescribeUserRequestById("tk.UserId")
	newuser := user.NewDescribeUserRequestById(fmt.Sprintf("%d", tk.UserId))
	u, err := i.user.DescirbeUser(ctx, newuser)
	if err != nil {
		return nil, err
	}
	tk.Role = u.Role

	return tk, nil
}
